﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Moonlite.ExtensionApi
{
    public static class ShellFileUtils
    {
        private static ImageList imageList;
        private static Dictionary<IconReference, int> iconMap = new Dictionary<IconReference, int>();
        private static Dictionary<string, string> fileTypeMap = new Dictionary<string, string>();
        private static SortedList<WindowsSpecialFolder, int> folderMap = new SortedList<WindowsSpecialFolder, int>();
        private static int directoryIcon;
        private static int fileIcon;
        private static int lvUp;
        public static int DirectoryIcon
        {
            get
            {
                if (directoryIcon > 0)
                {
                    return directoryIcon - 1;
                }
                int n = GetSpecialIcon(Path.GetTempPath(), FileAttributes.Directory);
                if (n >= 0)
                {
                    directoryIcon = n + 1;
                }
                return n;
            }
        }
        public static int FileIcon
        {
            get
            {
                if (fileIcon > 0)
                {
                    return fileIcon - 1;
                }
                string dummyPath = Path.Combine(Path.GetTempPath(), "Dummy");
                int n = GetSpecialIcon(dummyPath, FileAttributes.Normal);
                if (n >= 0)
                {
                    fileIcon = n + 1;
                }
                return n;
            }
        }
        public static ImageList ImageList
        {
            get { return imageList; }
        }
        public static string GetFileType(string extension)
        {
            if (string.IsNullOrEmpty(extension))
            {
                return "";
            }
            extension = extension.TrimStart('.');
            lock (fileTypeMap)
            {
                string rslt;
                if (fileTypeMap.TryGetValue(extension, out rslt))
                    return rslt;
            }
            string typeName = GetTypeNameForExtension(extension);
            lock (fileTypeMap)
            {
                fileTypeMap[extension] = typeName;
            }
            return typeName;
        }
        public static string GetTypeName(string path)
        {
            SHFILEINFO fileinfo = new SHFILEINFO();
            IntPtr rslt = UnsafeNativeMethods.SHGetFileInfoW(path, 0, ref fileinfo, (uint)Marshal.SizeOf(fileinfo), NativeMethods.SHGFI_TYPENAME);
            if (rslt == IntPtr.Zero)
            {
                return null;
            }
            return fileinfo.szTypeName;
        }
        public static string GetTypeNameForExtension(string extension)
        {
            Debug.Assert(!string.IsNullOrEmpty(extension));
            string dummyPath = Path.Combine(Path.GetTempPath(), "Dummy." + extension);
            SHFILEINFO fileinfo = new SHFILEINFO();
            IntPtr rslt = UnsafeNativeMethods.SHGetFileInfoW(dummyPath, (uint)(FileAttributes.Normal), ref fileinfo, (uint)Marshal.SizeOf(fileinfo), NativeMethods.SHGFI_TYPENAME | NativeMethods.SHGFI_USEFILEATTRIBUTES);
            if (rslt == IntPtr.Zero)
            {
                return null;
            }
            return fileinfo.szTypeName;
        }
        public static int GetOsIcon(string path)
        {
            SHFILEINFO fileinfo = new SHFILEINFO();
            IntPtr sysImageList = UnsafeNativeMethods.SHGetFileInfoW(path, 0, ref fileinfo, (uint)Marshal.SizeOf(fileinfo), NativeMethods.SHGFI_SHELLICONSIZE | NativeMethods.SHGFI_SYSICONINDEX | NativeMethods.SHGFI_SMALLICON);
            if (sysImageList == IntPtr.Zero)
            {
                return -1;
            }
            IconReference handle = new IconReference(sysImageList, fileinfo.iIcon);
            return ResolveReference(handle);
        }
        public static int GetSpecialIcon(string name, FileAttributes attr)
        {
            SHFILEINFO fileinfo = new SHFILEINFO();
            IntPtr sysImageList = UnsafeNativeMethods.SHGetFileInfoW(name, (uint)(int)attr, ref fileinfo, (uint)Marshal.SizeOf(fileinfo), NativeMethods.SHGFI_SHELLICONSIZE |
                NativeMethods.SHGFI_SYSICONINDEX | NativeMethods.SHGFI_SMALLICON | NativeMethods.SHGFI_USEFILEATTRIBUTES);
            if (sysImageList == IntPtr.Zero)
            {
                return -1;
            }
            IconReference handle = new IconReference(sysImageList, fileinfo.iIcon);
            return ResolveReference(handle);
        }
        public static int GetIconForExtension(string ext)
        {
            if (string.IsNullOrEmpty(ext))
            {
                return FileIcon;
            }
            return GetSpecialIcon("c:\\file." + ext.Trim('.'), FileAttributes.Normal);
        }
        public static int GetSpecialIcon(SpecialIcon icon)
        {
            EnsureSpecialImages();
            return lvUp - (int)(SpecialIcon.SortUp) + (int)icon;
        }
        public static int GetSpecialFolderIcon(Environment.SpecialFolder folder)
        {
            return GetSpecialFolderIcon((WindowsSpecialFolder)(int)folder);
        }
        public static int GetSpecialFolderIcon(WindowsSpecialFolder folder)
        {
            int index;
            if (folderMap.TryGetValue(folder, out index))
            {
                return index;
            }
            IntPtr pidl = IntPtr.Zero;
            try
            {
                if (0 != UnsafeNativeMethods.SHGetFolderLocation(IntPtr.Zero, folder, IntPtr.Zero, 0, out pidl))
                {
                    return -1;
                }
                SHFILEINFO fileinfo = new SHFILEINFO();
                IntPtr sysImageList = UnsafeNativeMethods.SHGetFileInfoW(pidl, (uint)(int)FileAttributes.Directory, ref fileinfo, (uint)Marshal.SizeOf(fileinfo),
                    NativeMethods.SHGFI_SHELLICONSIZE | NativeMethods.SHGFI_SYSICONINDEX | NativeMethods.SHGFI_SMALLICON | NativeMethods.SHGFI_PIDL);
                if (sysImageList == IntPtr.Zero)
                {
                    return -1;
                }
                IconReference handle = new IconReference(sysImageList, fileinfo.iIcon);
                return folderMap[folder] = ResolveReference(handle);
            }
            finally
            {
                if (pidl != IntPtr.Zero)
                    Marshal.FreeCoTaskMem(pidl);
            }
        }
        private static void EnsureSpecialImages()
        {
            if (lvUp != 0)
            {
                return;
            }
            Image imageStrip = Bitmap.FromStream(typeof(ShellFileUtils).Assembly.GetManifestResourceStream("Moonlite.ExtensionApi.Resources.UpDownListView.png"));
            int count = imageStrip.Width / 16;
            imageList.Images.AddStrip(imageStrip);
            lvUp = imageList.Images.Count - count + 1;
        }
        private static int ResolveReference(IconReference handle)
        {
            if (handle == null)
            {
                return -1;
            }
            int value;
            if (iconMap.TryGetValue(handle, out value))
            {
                return value;
            }
            using (handle)
            {
                IntPtr iconHandle = handle.GetHandle();
                if (iconHandle == IntPtr.Zero)
                {
                    return -1;
                }
                Icon icon;
                try
                {
                    icon = Icon.FromHandle(iconHandle);
                }
                catch (ArgumentException)
                {
                    return -1;
                }
                try
                {
                    imageList.Images.Add(icon);
                }
                catch (InvalidOperationException)
                {
                    return -1;
                }
            }
            int n = imageList.Images.Count - 1;
            iconMap.Add(handle, n);
            return n;
        }
        static ShellFileUtils()
        {
            imageList = new ImageList();
            imageList.ImageSize = new Size(16, 16);
            imageList.ColorDepth = ColorDepth.Depth32Bit;
        }
    }
}
